//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <PowerInc.h>
#include <VirtualDevice.h>
#include <TimeManager.h>

extern void TurnOffLCDAndTouch();
extern Boolean IsLcdBlack();
//object copy maybe a waste of time.fix it later!
ECode CVirtualDevice::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 uNumberOfBytesToRead,
    /* [out] */ MemoryBuf * pData,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec;
    if (!IgnoreRead) {
        ChangeMode(DevicePowerOn);
        if(!KeepOn)RestartTimer();
    }
    ec=piRealdev->Read(u64Offset,uNumberOfBytesToRead,pData,ppCompletionEvent);
    if(FAILED(ec))return ec;
    return ec;
}

ECode CVirtualDevice::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & ebbData,
    /* [out] */ Int32 * puNumberOfBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec;
    ChangeMode(DevicePowerOn);
    ec=piRealdev->Write(u64Offset,ebbData,puNumberOfBytesWritten,
        ppCompletionEvent);
    if(FAILED(ec))return ec;
    if(!KeepOn)RestartTimer();
    return ec;
}

ECode CVirtualDevice::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & ebbInData,
    /* [out] */ MemoryBuf * pOutData,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec;
//    if(IgnoreControl) PowerState=DevicePowerOn;//just log the state.
//    else   ChangeMode(DevicePowerOn);
    if(!IgnoreControl) ChangeMode(DevicePowerOn);
    ec=piRealdev->Control(nControlCode,ebbInData,pOutData,ppCompletionEvent);
    if(!KeepOn&&!IgnoreControl)RestartTimer();
    return ec;
}

void DM_ChangeMode(void * pdevice);
void SystemDoSleep(void * pdevice);
void PoDoManualSleep(Boolean Sleep);
Boolean PoGetLcdLockScreen();
void PoEventHandler(void * pArg);
Boolean IsDeviceCanPowerOff();
void SystemDoSleepByManual(void * pdevice);
void SystemDoSleepByMachine(void * pdevice);
EXTERN void ShowDeviceStatus();
extern CVirtualDevice * g_display;
extern CVirtualDevice * g_cpu;
extern CVirtualDevice * g_ptouch;
extern Boolean bManualLock;
extern UInt32 g_manualHighTime;
extern UInt32 g_manualLowTime;
extern UInt32 g_auto;
extern ECode EmPostEventByIsr(UInt32 uEventNum, PEmIsrParameter pEmIsrParm);

CVirtualDevice::CVirtualDevice(IInterface * pObject,
    unsigned long InitPowerState, Boolean IgnoreTimer, Boolean IgnoreControl)
{
    PowerState = InitPowerState;// first:PowerOn
    SpecifiedMode = 5;//set an illegal mode,means user app not specified one.
    ModeChangeInterval = 12;//default 12s,which is from lcd.
    KeepOn = FALSE;
    IgnoreRead = FALSE;
    this->IgnoreTimer = IgnoreTimer;
    this->IgnoreControl = IgnoreControl;
    piRealdev = (IDeviceDriver *)pObject;
    if (!IgnoreTimer)
        ChangeModeCallBack = DM_ChangeMode;
    else
        ChangeModeCallBack = SystemDoSleep;

    PowerCreateTimer(&hTimer,1,ModeChangeInterval,ChangeModeCallBack,
        (void *)this);//s
}

void CVirtualDevice::RestartTimer()
{
    PowerReStartTimer(hTimer,ModeChangeInterval);
}

void CVirtualDevice::RestartTimer(int Interval)
{
    if (Interval == 0)
        PowerShortTimer(hTimer); //Special
    else
        PowerReStartTimer(hTimer,Interval);
}

UInt32 CVirtualDevice::GetLeftInterval()
{
    long unsigned int ret;
    PowerGetLeftTime(hTimer,&ret);
    return ret;
}

ECode CVirtualDevice::SetMode(UInt32 mode)
{
//    MemoryBuf ebbintOut(&PowerState, 4, 4);
//    MemoryBuf_<4> ebbintIn;
//    ebbintIn.Copy(&mode, sizeof(int));
    unsigned long SaveState = PowerState;
    PowerState=6;//Invalid state

    ECode ec = piRealdev->Control(
                mode, NULL_MEMORYBUF, NULL, NULL );
    DebugOut(DebugPrint("in set mode %d\n",mode);
    )

    PowerState = SaveState;

    return ec;
}

//BUGBUG: MUST OPTIMIZE !
void CVirtualDevice::UpdateInputLastUse()
{//invoked by user event.
    if(KeepOn)return;

    if (SpecifiedMode == 5) {//user app not specfied,use mode 2.
        SpecifiedMode=DevicePowerOn;
    }
    ChangeMode((DeviceMode)SpecifiedMode);
        //for lcd ect.,a little different from common device like audio card.
    if (SpecifiedMode != (UInt32)DevicePowerOff) {
    	RestartTimer();
    }
}

ECode CVirtualDevice::ChangeMode(DeviceMode Mode)
{
    ECode ec = NOERROR;

    switch (Mode) {
        case DevicePowerOn:{
            if (PowerState == DevicePowerOff)
                ec = SetMode(1002); //PowerOn
            else if (PowerState == DeviceSuspend)
                    ec = SetMode(1001); //Resume
            else if (PowerState == DeviceLowPower)
                ec = SetMode(1011);//Low2On
            break;
        }
        case DeviceSuspend:{
            if (PowerState == DevicePowerOn || PowerState == DeviceLowPower)
                ec = SetMode(1000); //Suspend
            else
                goto Exit;
            break;
        }
        case DeviceLowPower:{//Only support by Display
            if (PowerState == DevicePowerOn)
                ec = SetMode(1010); //On2Low
            else {
                cprintf("Invalid LCDMod %d->%d, Keep %d\n",
                    PowerState, Mode, KeepOn);
                goto Exit;
            }
            break;
        }
        case DevicePowerOff:{//Only support by Display
            if (this == g_display) {
                ec = SetMode(1006);
                //g_ptouch->SetMode(2000);
                //g_ptouch->PowerState = DevicePowerOff;
            }
            break;
        }
        default:goto Exit;
    }

    if (FAILED(ec)) goto Exit;

    PowerState = Mode;

Exit:
    return ec;
}

ECode CVirtualDevice::ChangeModeByStep()
{//Only for LCD device
    ECode ec = NOERROR;
    if (KeepOn) {
        //cprintf("ERR: ChangeModeByStep %d-> while KeepOn\n", PowerState);
        KeepSpecifiedMode(DevicePowerOn, KeepOn);
        return ec;
    }

    if (PowerState == DevicePowerOn) {
        ec = ChangeMode(DeviceLowPower);
        if (FAILED(ec)) return ec;
        if (PoGetLcdLockScreen()) {//first come has been lock, indicate lock by manual
        	RestartTimer(g_manualLowTime);
        } else {//auto lock wait lock screen Event to black.
            RestartTimer(g_auto);
            //PowerStopTimer(g_display->hTimer);
        }
    }
    else if (PowerState == DeviceLowPower) {
        if (PoGetLcdLockScreen()) {
            if (!IsLcdBlack()) {
                TurnOffLCDAndTouch();
            }
        } else {
            EmIsrParameter Param;
            memset(&Param,0,sizeof(Param));
            EmPostEventByIsr(213, &Param);//notify apps auto lock screen event
            PowerStopTimer(g_display->hTimer);
        }
//        ec = ChangeMode(DeviceSuspend);
//        if (FAILED(ec)) return ec;
        PowerStopTimer(hTimer);
    }else if (PowerState == DevicePowerOff) {
		PowerStopTimer(hTimer);
    }
    return ec;
}

void CVirtualDevice::KeepSpecifiedMode(UInt32 Mode, Boolean KeepOn)
{
    this->KeepOn=KeepOn;
    if (KeepOn) {
        ChangeMode((DeviceMode)Mode);
        PowerStopTimer(hTimer);//stop the timer,keep the specified mode
    }
    else {//the user app want to use system power mode adjusting.
        ChangeMode((DeviceMode)Mode);
        if(Mode!=(UInt32)DevicePowerOff)RestartTimer();
    }
}

void CVirtualDevice::SetChangeModeInterval(UInt32 Interval)
{
    ModeChangeInterval=Interval;
    RestartTimer();
}

void CVirtualDevice::InitDevPowerMode(UInt32 mode)
{//not imp now.
    return;
}

void CVirtualDevice::SetChangeModeCallBack(TimerCallBackProc proc)
{
    ChangeModeCallBack = proc;
    PowerKillTimer(hTimer);
    PowerCreateTimer(&hTimer,1,ModeChangeInterval,ChangeModeCallBack,
        (void *)this);//s
}

void DM_ChangeMode(void * pdevice)
{//called by timer routine
    CVirtualDevice * prdev=(CVirtualDevice *)pdevice;
    DebugOut(DebugPrint("in dm change mode ,change from %d to %d\n",
        prdev->PowerState,DeviceSuspend);
    )
    prdev->ChangeMode(DeviceSuspend);
    PowerStopTimer(prdev->hTimer);
    g_cpu->RestartTimer(0);//Short cpu timer
}

void LCD_ChangeMode(void * pdevice)
{//called by timer routine
    CVirtualDevice * prdev=(CVirtualDevice *)pdevice;
    DebugOut(DebugPrint("in dm change mode ,change from %d to %d\n",
        prdev->PowerState,DeviceSuspend);
    )
    ECode ec = prdev->ChangeModeByStep();
    if (FAILED(ec)) prdev->RestartTimer();
}

#define LARGETIME 24*3600
void Bt_ChangeMode(void * pdevice)
{//called by timer routine
    CVirtualDevice * prdev=(CVirtualDevice *)pdevice;
    prdev->SetMode(1100);//notify
    prdev->RestartTimer(LARGETIME);//not suspend until bt stack tell us
}

void SystemDoSleep(void * pdevice)
{//call by timer routine
//    CVirtualDevice * prdev=(CVirtualDevice *)pdevice;
    DebugPrint("now decide whether the system can %s sleep\n",
        PoGetLcdLockScreen()?"manual":"auto");
    if (PoGetLcdLockScreen()) {
        SystemDoSleepByManual(pdevice);
        return ;
    }
    else {
        SystemDoSleepByMachine(pdevice);
        return ;
    }
//    if(IsDeviceCanPowerOff()) {
//        prdev->ChangeMode(DeviceSuspend);
//        prdev->PowerState=DevicePowerOn;
//        return ;
//    }
}

void SystemDoSleepByManual(void * pdevice)
{//call by timer routine
    CVirtualDevice * prdev=(CVirtualDevice *)pdevice;
    //ShowDeviceStatus();
    if (IsDeviceCanPowerOff()) {
        prdev->SetMode(1005);//by manual
        prdev->PowerState=DevicePowerOn;
        return;
    }
    else {
        ShowDeviceStatus();
    }
}

void SystemDoSleepByMachine(void * pdevice)
{//call by timer routine
    CVirtualDevice * prdev=(CVirtualDevice *)pdevice;
    //ShowDeviceStatus();
    if (IsDeviceCanPowerOff()) {
        prdev->SetMode(1000);//by machine
        prdev->PowerState=DevicePowerOn;
        prdev->KeepSpecifiedMode((UInt32)DevicePowerOn, TRUE);
        return;
    }
    else {
        ShowDeviceStatus();
    }
}
